import os
import urllib

import pymongo
import time
from loguru import logger
from pymongo.errors import DuplicateKeyError
import redis
from Configs.setting import Redis_HOST, Redis_PASSWORD, Redis_DB,Mongo_HOST, Mongo_PORT, Mongo_DB, Mongo_USERNAME, Mongo_PASSWORD

mongo_connect = {
    "host": Mongo_HOST,
    "port": Mongo_PORT,
    "db": Mongo_DB,
    "user": Mongo_USERNAME,
    "pwd": Mongo_PASSWORD,
}
mongo_connect_cloud = {
    "host": "192.168.44.131",
    "port": "27017",
    "db": "DataRelatedToLiveStreaming",
    "user": "DataRelatedToLiveStreaming_admin",
    "pwd": "XVbHhLYkcy7WCszd"
}

r = redis.Redis(Redis_HOST, password=Redis_PASSWORD, decode_responses=True, db=Redis_DB,) #  health_check_interval=30


class MyRetry:
    def __init__(self,
                 times=1,
                 return_obj=None,
                 return_msg=None,
                 msg_return_params=False,
                 return_err_cause=True,
                 show_process_err=True,
                 sleep_time=0
                 ):
        """
        重试模块
        :param times: 重试次数
        :param return_obj: 重试失败会返回的对象，如果参数为None则不返回直接raise，否则返回除None外指定对象
        :param return_msg: 自定义重试失败后打印的消息
        :param msg_return_params: 是否要打印传入的参数
        :param return_err_cause: 是否打印报错原因
        :param show_process_err: 是否打印重试过程中的提示
        :param sleep_time: 暂停时间
        """
        self.times = times
        self.return_obj = return_obj
        self.return_msg = return_msg
        self.msg_return_params = msg_return_params
        self.return_err_cause = return_err_cause
        self.show_process_err = show_process_err
        self.sleep_time = sleep_time

    def __call__(self, fun):
        retry_times = self.times
        return_obj = self.return_obj
        return_msg = self.return_msg
        msg_return_params = self.msg_return_params
        return_err_cause = self.return_err_cause
        show_process_err = self.show_process_err
        sleep_time = self.sleep_time

        def retry_handle(*args, **kwargs):
            msg = f'函数: {fun.__name__}   重试达到最大次数{retry_times}次'
            cause = ''
            for times in range(retry_times):
                try:
                    fun_res = fun(*args, **kwargs)
                except Exception as e:
                    cause = e
                    if times == retry_times - 1:
                        if return_obj is not None and return_msg is None:
                            msg += f' 返回自定义对象: {return_obj} '
                            return return_obj
                        elif return_obj is None and return_msg is not None:
                            msg += f' 消息: {return_msg} '
                        elif return_obj is not None and return_msg is not None:
                            msg += f' 返回自定义对象: {return_obj}  消息: {return_msg} '
                            return return_obj
                        else:
                            msg += f' 重试达到最大次数({retry_times}次 '
                            raise
                    else:
                        if show_process_err:
                            logger.error(f'函数: {fun.__name__} 发生异常:{e}，第{times + 1}次重试:')
                            # logger.debug(*args)
                        if sleep_time:
                            time.sleep(sleep_time)
                else:
                    return fun_res
                finally:
                    if times == retry_times - 1 and msg:
                        if msg_return_params:
                            msg += f' 参数: {args} {kwargs} '
                        if return_err_cause:
                            msg += f'原因: {cause}'
                        logger.warning(msg)

        return retry_handle


class MongoClient:
    def __init__(self,mongo_connect):
        try:
            # username = urllib.parse.quote_plus(mongo_connect["user"])
            username = mongo_connect["user"]
            # password = urllib.parse.quote_plus(mongo_connect["pwd"])
            password = mongo_connect["pwd"]
            if mongo_connect.get('user'):
                self.client = pymongo.MongoClient(
                    f'mongodb://{username}:{password}@{mongo_connect["host"]}:{mongo_connect["port"]}/{mongo_connect["db"]}'
                )
            else:
                self.client = pymongo.MongoClient(f'mongodb://{mongo_connect["host"]}:{mongo_connect["port"]}',)
            self.db = self.client[mongo_connect["db"]]
            logger.success("MongoDB连接成功！")
        except Exception as e:
            logger.error(f"MongoDB连接失败！{e}")
            os._exit(0)

    # def aggregate(self, col, find_list, **kwargs):
    #     try:
    #         return (True, self.db[col].aggregate(find_list, **kwargs))
    #     except Exception as e:
    #         logger.warning(e)
    #         return (False, e)

    def insert_document(self, col, document, Type=1):
        '''
        进行文档插入，插入成功返回 _id 否则返回False
        :param document: 当单一插入的时候是字典，否则是列表[{}]
        :return:
        '''
        if Type == 1:
            try:
                return (True, self.db[col].insert_one(document))
            except DuplicateKeyError as e:
                print(e)
                return (True, '')
            except Exception as e:
                logger.warning(e)
                return (False, e)

        elif Type == 3:
            for one_data in document:
                try:
                    self.db[col].insert_one(one_data)
                except DuplicateKeyError as e:
                    logger.warning(e)
                    continue
                except Exception as e:
                    logger.warning(e)
                    return (False, e)
            return (True, '')
        else:
            try:
                return (True, self.db[col].insert_many(document))
            except Exception as e:
                logger.error(f"写入数据失败，{e}")
                return (False, e)

    def update_document(self, col, query, update, Type=1):
        '''
        进行文档更新
        :param col: 集合
        :param query: 一个与要更新的文档匹配的查询
        :param update: 要应用的修改
        :return:
        '''
        if Type == 1:
            try:
                return (True, self.db[col].update_one(query, update))
            except Exception as e:
                logger.error(f"更新失败，失败原因：{e}")
                return (False, Exception)
        else:
            try:
                return (True, self.db[col].update_many(query, update))
            except Exception as e:
                logger.error(f"更新失败，失败原因：{e}")
                return (False, e)

    def update_different_filter_document(self, col, update_list):
        try:
            return (True, self.db[col].bulk_write([pymongo.UpdateOne(i[0], i[1]) for i in update_list]))
        except Exception as e:
            logger.error(f"更新失败，失败原因：{e}")
            return (False, Exception)

    def find_document(self, col, query, projection=None, Type=1):
        '''
        进行文档查询
        :param query:   查询条件，必须是
        :param Tpye: 0/1    1查询单一文档，0查询所有文档
        :return:
        '''
        if Type == 1:
            try:
                find_res = self.db[col].find_one(query, projection=projection)
                if find_res:
                    return (True, find_res)
                else:
                    return (False, [])
            except Exception:
                logger.warning(Exception)
                return (False, [])
        else:
            try:
                return (True, self.db[col].find(query, projection=projection))
            except Exception:
                logger.warning(Exception)
                return (False, [])
    def find_document_new(self,col,query,is_count=False,*args,**kwargs):
        '''
        进行文档查询
        :param query:   查询条件，必须是
        :param Tpye: 0/1    1查询单一文档，0查询所有文档
        :return:
        '''
        Type = kwargs.get('Type',1)
        if kwargs.get('Type'):
            del kwargs['Type']
        try:
            if Type == 1:
                find_res = self.db[col].find_one(query,*args,**kwargs)
            else:

                find_res = self.db[col].find(query,*args, **kwargs)
            if find_res:
                if is_count:
                    return (True,find_res.count()) #这是添加的
                else:
                    return (True,find_res)
            else:
                return (False,[])
        except Exception as e:
            logger.warning(e)
            return (False, [])

    def find_document_new(self,col,query,*args,**kwargs):
        '''
        进行文档查询
        :param query:   查询条件，必须是
        :param Tpye: 0/1    1查询单一文档，0查询所有文档
        :return:
        '''
        Type = kwargs.get('Type',1)
        if kwargs.get('Type'):
            del kwargs['Type']
        try:
            if Type == 1:
                find_res = self.db[col].find_one(query,*args,**kwargs)
            else:
                find_res = self.db[col].find(query, *args, **kwargs)
            if find_res:
                return (True,find_res)
            else:
                return (False,[])
        except Exception as e:
            logger.warning(e)
            return (False, [])

    def remove_document(self, col, query, Type=1):
        if Type == 1:
            try:
                return (True, self.db[col].delete_one(query))
            except Exception:
                logger.warning(Exception)
                return (False, [])
        else:
            try:
                return (True, self.db[col].delete_many(query))
            except Exception:
                logger.warning(Exception)
                return (False, [])

    def success_update_db(self, col, md5):
        result = self.update_document(col, {"md5": md5}, {"$set": {"is_down": 1}})
        if result[0]:
            return True
        return False

    @MyRetry(times=3, return_obj=False, return_msg="更新mongodb 数据失败！")
    def faild_update_db(self, col, md5, is_down=1):
        result = self.update_document(col, {"md5": md5}, {"$set": {"is_down": is_down}})
        if result[0]:
            return True
        return False

    def distinct(self, table_name, field_name,query_info):
        col_ = self.db[table_name]
        res = col_.distinct(field_name,query_info)
        return res

    def aggregate(self, table_name, find_list):
        col_ = self.db[table_name]
        res = col_.aggregate(find_list,allowDiskUse=True)
        return [i for i in res]

mongo_connect3={
    "host":'192.168.44.131',
    "user":'ChineseJournals_write',
    "pwd":'CpMp2wbPHVzBp6nU',
    'port':27017,
    "db":'ChineseJournals',
}
mongo_client = MongoClient(mongo_connect)
mongo_client_cloud = MongoClient(mongo_connect_cloud)
mongo_client_chinesejournals = MongoClient(mongo_connect3)
